use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;
use std::env;


pub fn time_info(time: &str) -> String {
    // The current code is only supported from 1900 to 2100, because of the data of spring festival, but this one is easy to change
    let (y, m, d) = get_ymd(time);
    let week = cul_week(y, m, d);
    let left_days = cul_left_days(y, m, d);
    let new_year = cul_new_year(y, m, d);
    format!("{},{},{}", week, left_days, new_year)
}

fn get_ymd(time: &str) -> (u64, u64, u64) {
    let times: Vec<&str> = time.split('-').collect();
    (u64::from_str(times[0]).unwrap(), u64::from_str(times[1]).unwrap(), u64::from_str(times[2]).unwrap())
}

fn is_leap(y: u64) -> bool {
    y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)
}

fn days_in_month(y: u64, m: u64) -> u64 {
    match m {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
        4 | 6 | 9 | 11 => 30,
        2 => match is_leap(y) {
            true => 29,
            false => 28,
        },
        _ => 31,
    }
}

fn days_in_year(y: u64) -> u64 {
    match is_leap(y) {
        true => 366,
        false => 365,
    }
}

fn cul_past_days(y: u64, m: u64, d: u64) -> u64 {
    let mut res = 0;
    for i in 1..m {
        res += days_in_month(y, i);
    }
    res + d
}

fn cul_weekday(y: u64, m: u64, d: u64) -> u64 {
    // use Zeller to cul
    // W = [C/4] - 2C + y + [y/4] + [13 * (M+1） / 5] + d - 1
    let (y, m) = match m < 3 {
        true => (y - 1, m + 12),
        false => (y, m),
    };
    let c = y / 100;
    let y = y % 100;
    (c / 4 + y + y / 4 + 13 * (m + 1) / 5 + d - 2 * c - 1) % 7
}

fn cul_week(y: u64, m: u64, d: u64) -> u64 {
    // ISO ruler, First thursday is first week in year
    let first_day_weekday = cul_weekday(y, 1, 1);
    let first_week_start: i64 = match first_day_weekday <= 4 {
        true => 1 - (first_day_weekday as i64) + 1,
        false => 8 - (first_day_weekday as i64) + 1,
    };
    let next_first_day_weekday = cul_weekday(y + 1, 1, 1);
    let days = days_in_year(y);
    let last_week_end = match next_first_day_weekday <= 4 {
        true => days + 1 - next_first_day_weekday,
        false => days + 8 - next_first_day_weekday,
    };
    let past_days = cul_past_days(y, m, d);
    if past_days > last_week_end {
        return 1;
    }
    if (past_days as i64) < first_week_start {
        let pre_days = days_in_year(y - 1);
        let days = past_days + pre_days;
        let pre_first_day_weekday = cul_weekday(y - 1, 1, 1);
        let pre_first_week_start: i64 = match pre_first_day_weekday <= 4 {
            true => 1 - (pre_first_day_weekday as i64) + 1,
            false => 8 - (pre_first_day_weekday as i64) + 1,
        };
        return (((days as i64) - pre_first_week_start) / 7 + 1) as u64;
    }
    (((past_days as i64) - first_week_start) / 7 + 1) as u64
}

fn cul_left_days(y: u64, m: u64, d: u64) -> u64 {
    let days = days_in_year(y);
    days - cul_past_days(y, m, d)
}


fn find_workspace_root() -> Option<String> {
    let mut current_dir = env::current_dir().ok()?;
    let cargo_toml_path = current_dir.join("Cargo.toml");
    if cargo_toml_path.is_file() {
        let mut file = File::open(cargo_toml_path).unwrap();
        let mut json = String::new();
        file.read_to_string(&mut json).unwrap();
        if json.contains("[workspace]") {
            return Some(current_dir.to_str().unwrap().to_string());
        }
    }

    while current_dir.parent().is_some() {
        current_dir = current_dir.parent()?.to_path_buf();
        let cargo_toml_path = current_dir.join("Cargo.toml");
        if cargo_toml_path.is_file() {
            let mut file = File::open(cargo_toml_path).unwrap();
            let mut json = String::new();
            file.read_to_string(&mut json).unwrap();
            if json.contains("[workspace]") {
                return Some(current_dir.to_str().unwrap().to_string());
            }
        }
    }

    None
}

fn cul_new_year(y: u64, m: u64, d: u64) -> u64 {
    let workspace = PathBuf::from(find_workspace_root().unwrap());
    let file_path = "topic1/solutiont3/src/spring_festival.json";
    let path = workspace.join(file_path);
    let mut file = File::open(path).unwrap();
    let mut json = String::new();
    file.read_to_string(&mut json).unwrap();
    let data: Vec<(u64, u64, u64)> = serde_json::from_str(&json).unwrap();
    // Data from 1900 to 2100
    let offset = (y - 1900) as usize;
    let (mut nm, mut nd) = (data[offset].1, data[offset].2);
    if nm < m || (nm == m && nd < d) {
        let offset = (y - 1900 + 1) as usize;
        (nm, nd) = (data[offset].1, data[offset].2);
        return cul_left_days(y, m, d) + cul_past_days(y, nm, nd) - 1;
    }
    cul_past_days(y, nm, nd) - cul_past_days(y, m, d) - 1
}